#pragma once

#include <functional>
#include "sock.hpp"

namespace select_ns
{
    class SelectServer
    {
        const static uint16_t defaultport = 8080;
        const int fdnum = sizeof(fd_set) * 8;
        const int defaultfd = -1;
        using func_t = std::function<std::string(const std::string)>;

    public:
        SelectServer(func_t f, int16_t port = defaultport)
            : _port(port), _listensock(-1), fdarry(nullptr), func(f)
        {
        }
        void Print()
        {
            std::cout << "fd list: ";
            for (int i = 0; i < fdnum; i++)
            {
                if (fdarry[i] != defaultfd)
                    std::cout << fdarry[i] << " ";
            }
            std::cout << std::endl;
        }
        void initServer()
        {
            _listensock = Sock::Socket();
            Sock::Bind(_listensock, _port);
            Sock::Listen(_listensock);
            fdarry = new int[fdnum];
            // 初始化fdarry
            for (int i = 0; i < fdnum; i++)
            {
                fdarry[i] = defaultfd;
            }
            fdarry[0] = _listensock; // 不变了
        }
        void Accepter()
        {
            // select 告知我，listensock就绪了
            std::string clientip;
            uint16_t clientport;
            // accept一定不会阻塞
            int sock = Sock::Accept(_listensock, &clientip, &clientport); // accept = 等 + 获取，让select只负责等，accept只负责获取
            if (sock < 0)
                return;
            logMessage(NORMAL, "accept success [%s][%d]", clientip.c_str(), clientport);
            // 开始进行服务器的业务处理逻辑
            // ......

            // sock 能直接进行recv/read吗？不能，因为sock底层缓冲区是否就绪不知道。只有select有资格检测事件是否就绪！
            // 所以，也要把新的fd托管给select
            // 本质就是将新的fd添加到fdarry中
            int i = 0;
            for (; i < fdnum; i++)
            {
                if (fdarry[i] != defaultfd)
                    continue;
                else
                    break;
            }
            // 找到了一个可以用的下标保存新的fd
            if (i == fdnum)
            {
                logMessage(WARNING, "server is full, please wait");
                close(sock);
            }
            else
            {
                fdarry[i] = sock;
            }
            Print();
        }
        void ServiceIO(int sock, int pos)
        {
            // 1.读取
            char buffer[1024];
            ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
            if (s > 0)
            {
                buffer[s] = 0;
                logMessage(NORMAL, "client# %s", buffer);
            }
            else if (s == 0)
            {
                close(sock);
                fdarry[pos] = defaultfd;
                logMessage(NORMAL, "client quit");
                return;
            }
            else
            {
                close(sock);
                fdarry[pos] = defaultfd;
                logMessage(ERROR, "client quit,%s", strerror(errno));
                return;
            }
            // 2.处理
            // write bug
            std::string response = func(buffer);
            write(sock, response.c_str(), response.size());
        }
        void HandlerReadEvent(fd_set &rfds)
        {
            for (int i = 0; i < fdnum; i++)
            {
                if (fdarry[i] == defaultfd)
                    continue;

                if (FD_ISSET(fdarry[i], &rfds))
                {
                    std::cout << fdarry[i] << std::endl;

                    if (fdarry[i] == _listensock)
                    {
                        Accepter();
                    }
                    else
                    {
                        ServiceIO(fdarry[i], i);
                    }
                }
            }
        }

        void start()
        {

            for (;;)
            {
                fd_set rfds;
                FD_ZERO(&rfds);
                int maxfd = fdarry[0];
                for (int i = 0; i < fdnum; i++)
                {
                    if (fdarry[i] == defaultfd)
                        continue;
                    FD_SET(fdarry[i], &rfds); // 将所有合法fd全部添加到读文件描述符集中
                    if (fdarry[i] > maxfd)
                        maxfd = fdarry[i]; // 更新所有fd最大的fd
                }
                // struct timeval timeout = {3, 0};
                // int n = select(_listensock + 1, &rfds, nullptr, nullptr, &timeout);
                // 一般使用select，需要程序员自己维护一个保存所有合法fd的数组
                int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);
                switch (n)
                {
                case 0:
                    logMessage(NORMAL, "timeout... ");
                    break;
                case -1:
                    logMessage(WARNING, "select error, code: %d, err string: %s", errno, strerror(errno));
                    break;
                default:
                    // 说明有事件就绪了，目前只有一个监听事件就绪
                    logMessage(NORMAL, "have event ready... ");
                    HandlerReadEvent(rfds);
                    break;
                }
            }
        }
        ~SelectServer()
        {
            if (_listensock >= 0)
                close(_listensock);
            if (fdarry)
                delete[] fdarry;
        }

    private:
        int _port;
        int _listensock;
        int *fdarry;
        func_t func;
    };

}